import { ITerminalAddon, Terminal } from '@xterm/xterm';

import { Disposable, Emitter } from '../../utils';

import { ITerminalConnection, ITerminalService } from '../common';

/**
 * 终端 Attach 插件，用于将终端与后端连接，处理数据收发、事件分发等
 */
export class AttachAddon extends Disposable implements ITerminalAddon {
  internalService: ITerminalService | undefined;
  connection: ITerminalConnection | undefined; // 当前终端连接
  private _disposeConnection: Disposable | null; // 连接的可释放对象
  private _terminal: Terminal; // xterm 终端实例

  private _onData = new Emitter<string | ArrayBuffer>(); // 数据事件
  onData = this._onData.event;

  private _onExit = new Emitter<number | undefined>(); // 退出事件
  onExit = this._onExit.event;

  private _onTime = new Emitter<number>(); // 响应时间事件
  onTime = this._onTime.event;

  private _lastInputTime = 0; // 最后一次输入时间戳

  private readonly _onBeforeProcessData = new Emitter<{ data: string }>(); // 数据处理前事件
  readonly onBeforeProcessData = this._onBeforeProcessData.event;

  /**
   * 设置终端连接
   * @param internalService
   * @param connection 终端连接对象
   */
  public setConnection(
    internalService: ITerminalService | undefined,
    connection: ITerminalConnection | undefined
  ) {
    if (this._disposeConnection) {
      this._disposeConnection.dispose();
      this._disposeConnection = null;
    }
    this.internalService = internalService;
    this.connection = connection;
    if (connection) {
      // // 监听连接数据事件
      // this._disposeConnection = new Disposable(
      //   connection.onData((data: string | ArrayBuffer) => {
      //     let dataToWrite = data;
      //     if (typeof data === 'string') {
      //       const beforeProcessDataEvent = { data } as { data: string };
      //       // 通过 EventEmitter 修改终端 data 的内容
      //       this._onBeforeProcessData.fire(beforeProcessDataEvent);
      //
      //       if (beforeProcessDataEvent.data !== undefined) {
      //         dataToWrite = beforeProcessDataEvent.data;
      //       }
      //     }
      //
      //     this._onData.fire(dataToWrite);
      //
      //     // 写入终端
      //     this._terminal.write(
      //       typeof dataToWrite === 'string'
      //         ? dataToWrite
      //         : new Uint8Array(dataToWrite, 0, dataToWrite.byteLength)
      //     );
      //
      //     // connection.onData 的时候对 lastInputTime 进行差值运算，统计最后一次输入到收到回复的时间间隔
      //     if (this._lastInputTime) {
      //       const delta = Date.now() - this._lastInputTime;
      //       this._lastInputTime = 0;
      //       this._onTime.fire(delta);
      //     }
      //   })
      // );
      // if (connection.onExit) {
      //   // 监听连接退出事件
      //   this._disposeConnection.addDispose(
      //     connection.onExit((code) => {
      //       this._onExit.fire(code);
      //     })
      //   );
      // }

      const sessionId = this.connection?.sessionId;

      if (sessionId) {
        // 用 electron 事件订阅替代 connection 自身的 onData/onExit
        this._disposeConnection = new Disposable();

        // 订阅终端数据事件
        const dataDisposable = window.electron.terminalEvents.onSessionData(
          sessionId,
          (id, data) => {
            let dataToWrite: string | ArrayBuffer = data;

            if (typeof data === 'string') {
              const beforeProcessDataEvent = { data } as { data: string };
              this._onBeforeProcessData.fire(beforeProcessDataEvent);

              if (beforeProcessDataEvent.data !== undefined) {
                dataToWrite = beforeProcessDataEvent.data;
              }
            }

            this._onData.fire(dataToWrite);

            this._terminal.write(
              typeof dataToWrite === 'string'
                ? dataToWrite
                : new Uint8Array(dataToWrite, 0, dataToWrite.byteLength)
            );

            // lastInputTime 统计逻辑
            if (this._lastInputTime) {
              const delta = Date.now() - this._lastInputTime;
              this._lastInputTime = 0;
              this._onTime.fire(delta);
            }
          }
        );
        this._disposeConnection.addDispose(dataDisposable);

        // 订阅终端退出事件
        const exitDisposable = window.electron.terminalEvents.onSessionExit(
          sessionId,
          (id, code) => {
            this._onExit.fire(code);
          }
        );
        this._disposeConnection.addDispose(exitDisposable);
      }
    }
  }

  /**
   * 激活插件，绑定终端实例
   * @param terminal xterm 终端实例
   */
  public async activate(terminal: Terminal): Promise<void> {
    this._terminal = terminal;
    // 释放时自动清理连接
    this.addDispose(
      Disposable.create(() => this._disposeConnection?.dispose())
    );
    // 监听终端输入数据
    this.addDispose(terminal.onData((data) => this._sendData(data)));
    // 监听终端二进制数据
    this.addDispose(terminal.onBinary((data) => this._sendBinary(data)));
  }

  /**
   * 发送字符串数据到后端
   * @param data 字符串数据
   */
  private _sendData(data: string): void {
    if (!this.internalService) {
      return;
    }
    if (!this.connection || this.connection.readonly) {
      return;
    }
    // 记录 lastInputTime，用于终端反应速度统计
    this._timeResponse();
    // this.connection.sendData(data);
    this.internalService.sendText(this.connection.sessionId, data);
  }

  /**
   * 发送二进制数据到后端
   * @param data 二进制字符串
   */
  private _sendBinary(data: string): void {
    if (!this.internalService) {
      return;
    }
    if (!this.connection || this.connection.readonly) {
      return;
    }
    const buffer = new Uint8Array(data.length);
    for (let i = 0; i < data.length; ++i) {
      buffer[i] = data.charCodeAt(i) & 255;
    }
    this._timeResponse();
    // this.connection.sendData(buffer);
    this.internalService.sendText(this.connection.sessionId, buffer);
  }

  /**
   * 记录本次输入时间，用于统计响应时间
   */
  private _timeResponse() {
    this._lastInputTime = Date.now();
  }
}

// 默认终端行列数
export const DEFAULT_ROW = 80;
export const DEFAULT_COL = 24;
